/*           Copyright(c) 2005-2008 Dave Sexton            *
 *                   HtmlEditor Control                    *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace DaveSexton.Controls.Native
{
	/// <summary>
	/// Wraps an <c>OLECMDTEXT</c> structure and pins it in memory for use with COM-Interop calls.
	/// </summary>
	public sealed class OleCommandText : IDisposable
	{
		#region Public Properties
		public IntPtr Handle
		{
			get
			{
				return handle.AddrOfPinnedObject();
			}
		}

		public OleCommandTextType Query
		{
			get
			{
				return query;
			}
		}

		public string Text
		{
			get
			{
				int size = unchecked((int) info.cwActual);

				if (size == 0)
					return string.Empty;

				StringBuilder text = new StringBuilder(size);

				for (int i = 0; i < size; i++)
					text.Append((char) Marshal.ReadInt16(textPtr));

				return text.ToString();
			}
		}
		#endregion

		#region Private / Protected
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
		private readonly IntPtr textPtr;
		private readonly OleCommandTextType query;
		private readonly GCHandle handle;
		private readonly OLECMDTEXT info;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="OleCommandText" /> class.
		/// </summary>
		public OleCommandText(int textLength, OleCommandTextType query)
		{
			if (!Enum.IsDefined(typeof(OleCommandTextType), query))
				throw new System.ComponentModel.InvalidEnumArgumentException("query", (int) query, typeof(OleCommandTextType));

			this.query = query;

			textPtr = Marshal.AllocHGlobal(checked(textLength * 2));
			info = new OLECMDTEXT(textLength, query, textPtr);
			handle = GCHandle.Alloc(info, GCHandleType.Pinned);
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="OleCommandText" /> class.
		/// </summary>
		public OleCommandText()
		{
			textPtr = IntPtr.Zero;
			info = new OLECMDTEXT();
			handle = GCHandle.Alloc(info, GCHandleType.Pinned);
		}
		#endregion

		#region IDisposable Members
		/// <summary>
		/// Releases all resources used by an instance of the <see cref="OleCommandText" /> class.
		/// </summary>
		/// <remarks>
		/// This method calls the virtual <see cref="Dispose(bool)" /> method, passing in <strong>true</strong>, and then suppresses 
		/// finalization of the instance.
		/// </remarks>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// Releases unmanaged resources before an instance of the <see cref="OleCommandText" /> class is reclaimed by garbage collection.
		/// </summary>
		/// <remarks>
		/// This method releases unmanaged resources by calling the virtual <see cref="Dispose(bool)" /> method, passing in <strong>false</strong>.
		/// </remarks>
		~OleCommandText()
		{
			Dispose(false);
		}

		/// <summary>
		/// Releases the unmanaged resources used by an instance of the <see cref="OleCommandText" /> class and optionally releases the managed resources.
		/// </summary>
		/// <param name="disposing"><strong>true</strong> to release both managed and unmanaged resources; <strong>false</strong> to release only unmanaged resources.</param>
		private void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (handle != null)
					handle.Free();
			}

			if (textPtr != IntPtr.Zero)
				Marshal.FreeHGlobal(textPtr);
		}
		#endregion

		#region Nested
		[StructLayout(LayoutKind.Sequential)]
		private sealed class OLECMDTEXT
		{
			public uint cmdtextf;
			public uint cwActual;
			public uint cwBuf;
			public IntPtr rgwz;

			public OLECMDTEXT()
			{
				rgwz = IntPtr.Zero;
			}

			public OLECMDTEXT(int textLength, OleCommandTextType query, IntPtr text)
			{
				cwBuf = unchecked((uint) textLength);
				cmdtextf = (uint) query;
				rgwz = text;
			}
		}
		#endregion
	}
}
